package com.dd.cloud.user.service.phar.pharservice;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.dd.cloud.common.constants.DrugsConstants;
import com.dd.cloud.common.constants.PharConstants;
import com.dd.cloud.common.constants.PresConstants;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.utils.BigDecimalUtils;
import com.dd.cloud.common.vo.LoginInfo;
import com.dd.cloud.user.DTO.CancelPresReq;
import com.dd.cloud.user.entity.drugs.DrugsOutbound;
import com.dd.cloud.user.entity.order.OrderPay;
import com.dd.cloud.user.entity.phar.Pharmacy;
import com.dd.cloud.user.entity.pres.PresPrescription;
import com.dd.cloud.user.entity.pres.PresPretrialDrugs;
import com.dd.cloud.user.service.drugs.IDrugsOutboundService;
import com.dd.cloud.user.service.log.ILogErrorService;
import com.dd.cloud.user.service.phar.IPharmacyService;
import com.dd.cloud.user.service.pres.IPresPrescriptionService;
import com.dd.cloud.user.service.pres.IPresPretrialDrugsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description: //药房操作 装饰器执行类
 * @ClassName: PharDecoratorServiceExecute
 * @Author: czg
 * @Date: 2023/04/20 14:45
 * @Version: 1.0
 */
@Service
@Slf4j
public class PharDecoratorServiceExecute extends PharDecoratorService {

    @Autowired
    private IDrugsOutboundService drugsOutboundService;
    @Autowired
    private IPresPrescriptionService presPrescriptionService;
    @Autowired
    private IPresPretrialDrugsService presPretrialDrugsService;
    @Autowired
    private ILogErrorService logErrorService;
    @Autowired
    private IPharmacyService pharmacyService;
    @Autowired
    private PharServiceBeanInterface pharServiceBeanInterface;

    /**
     * 发送处方到药房 需要药品出库
     */
    @Override
    public boolean sendPresToPhar(OrderPay pay) {
        boolean b = super.sendPresToPhar(pay);

        PresPrescription pres = presPrescriptionService.getById(pay.getServerNo());
        if (pres == null) {
            logErrorService.presBackLog("处方编码不存在", "药品出库异常", "jsonBody=" + pres.toString());
            return true;
        }
        if (!b) {
            //处方发送药房失败, 自用药房还需要解锁库存
            Pharmacy pharmacy = pharmacyService.getById(pres.getPharmId());
            if (pharmacy == null) {
                return b;
            }
            if (pharmacy.getFlag() == PharConstants.Flag.STORE) {
                //平台第三方药房, 需要管理库存
                presPrescriptionService.releasePresDrugsStock(pres, LoginInfo.UserType.ADMIN);
            }

            /**
             * 重试机制, 异步重试 重试时间为2s, 10s, 60s
             */
            UpdateWrapper<PresPrescription> uw = new UpdateWrapper<>();
            uw.lambda().eq(PresPrescription::getTencode, pres.getTencode());
            // 创建一个定时任务
            Runnable task = () -> {
                PharServiceInterface pharService = pharServiceBeanInterface.getPharService(pres.getPharmId());
                pharService.sendPresToPhar(pay);
            };
            //异步任务线程池
            ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1);
            long delay = 0;
            log.error("发送处方异常重试, {}, {}", pres.getTencode(), pres.getTryApiNum());
            switch (pres.getTryApiNum()){
                case 1:
                    //异常请求一次, 2秒后重试
                    uw.lambda().set(PresPrescription::getTryApiNum, 2);
                    presPrescriptionService.update(uw);
                    delay = 2000;
                    // 提交任务
                    executor.schedule(task, delay, TimeUnit.MILLISECONDS);
                    break;
                case 2:
                    //异常请求2次, 10秒后重试
                    uw.lambda().set(PresPrescription::getTryApiNum, 3);
                    presPrescriptionService.update(uw);
                    delay = 10000;
                    executor.schedule(task, delay, TimeUnit.MILLISECONDS);
                    break;
                case 3:
                    //异常请求3次 1分钟后重试
                    uw.lambda().set(PresPrescription::getTryApiNum, 4);
                    presPrescriptionService.update(uw);
                    delay = 60000;
                    executor.schedule(task, delay, TimeUnit.MILLISECONDS);
                    break;
                default:
                    //超过3次 不继续重试

            }
            return b;
        }

        /**
         * 药品出库
         */
        QueryWrapper<PresPretrialDrugs> qw = new QueryWrapper<>();
        qw.lambda().eq(PresPretrialDrugs::getTencode, pay.getServerNo()).eq(PresPretrialDrugs::getStatus, PresConstants.PresDrugsStatus.NOMER);
        List<PresPretrialDrugs> list = presPretrialDrugsService.list(qw);
        if (list == null || list.isEmpty()) {
            logErrorService.presBackLog("处方药品不存在", "药品出库异常", "jsonBody=" + pres);
            return true;
        }
        list.forEach(drug -> {
            DrugsOutbound drugsOutbound = new DrugsOutbound();
            BeanUtils.copyProperties(drug, drugsOutbound);
            drugsOutbound.setCreateDate(LocalDateTime.now());
            drugsOutbound.setOrderNo(pay.getOrderNo());
            double price = drug.getRetailprice();
            if (drug.getDiscountPrice() != null) {
                price = drug.getDiscountPrice();
            }
            drugsOutbound.setOutboundPrice(BigDecimalUtils.multiply(new BigDecimal(price), new BigDecimal(pres.getAllDose())));
            drugsOutbound.setOutQty(BigDecimalUtils.multiply(new BigDecimal(drug.getDrugallnum()), new BigDecimal(pres.getAllDose())));
            drugsOutbound.setPrice(new BigDecimal(price));
            drugsOutbound.setPharId(pres.getPharmId());
            drugsOutbound.setUseUnit(drug.getUnit());
            boolean save = drugsOutboundService.save(drugsOutbound);
            if (!save) {
                logErrorService.presBackLog("处方药品添加出库失败", "药品出库异常", "jsonBody=" + drugsOutbound.getDrugnum());
            }
        });
        //处方支付成功 扣减库存及解锁库存
        Pharmacy pharmacy = pharmacyService.getById(pres.getPharmId());
        if (pharmacy == null) {
            return b;
        }
        if (pharmacy.getFlag() == PharConstants.Flag.STORE) {

            presPrescriptionService.subQty(pres);
        }
        return true;
    }

    /**
     * 取消处方 药品取消出库
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelPres(CancelPresReq req) {
        super.cancelPres(req);
        PresPrescription pres = presPrescriptionService.getById(req.getTencode());
        if (pres == null) {
            throw new ZekeException("处方编码不存在");
        }
        Pharmacy pharmacy = pharmacyService.getById(pres.getPharmId());
        if (pharmacy == null) {
            throw new ZekeException("药房不存在");
        }
        QueryWrapper<DrugsOutbound> qw = new QueryWrapper<>();
        qw.lambda().eq(DrugsOutbound::getTencode, req.getTencode()).eq(DrugsOutbound::getStatus, DrugsConstants.OutboundStatus.NOMAL);
        List<DrugsOutbound> list = drugsOutboundService.list(qw);
        if (list == null || list.isEmpty()) {
            return;
        }
        list = list.stream().peek(item -> item.setStatus(DrugsConstants.OutboundStatus.DEL)).collect(Collectors.toList());
        drugsOutboundService.updateBatchById(list);
        if (pharmacy.getFlag() == PharConstants.Flag.STORE) {
            //平台第三方药房, 需要管理库存
            presPrescriptionService.releasePresDrugsStock(pres, LoginInfo.UserType.DOCTOR);
        }
    }
}
